Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support a more generic ipxe installer #192

Merged
merged 7 commits into from
Aug 12, 2021

Conversation

mikemrm
Copy link
Contributor

@mikemrm mikemrm commented Aug 10, 2021

Description

Allows more generic support for ipxe installers for installation of an OS which cannot be modified to work with the OSIE installation method.

The new ipxe installer works similar to custom_ipxe however differs in that it is selected earlier in the process for different handling of data and which is used for any os which has the installer set to ipxe.

Why is this needed

Some os installation processes have unique installation environments which do not lend themselves easily to osie liveos / workflow setup as seen with other installers in boots like vmware and coreos. However instead of hard coding a new OS into boots, this method allows for those dynamic configurations to be handled separately by allowing the installer to be set to ipxe which supports chaining or injecting a script to boot the installer.

The model has changed for packet.OperatingSystem to now have two additional optional fields Installer and InstallerData.

The ipxe installer is configurable through the InstallerData field which it expects to be a string encoded JSON blob of which two fields may be specified chain which should be a full URL to a valid iPXE script and script which allows for injecting an ipxe script directly.

Example of both configurable fields:

{
  "chain": "https://boot.example.com/unique_os_installer.ipxe",
  "script": "echo Welcome to unique os installer\nshell\n"
}

Example using iPXE installer with chain loading:

{
  "distro": "Unique OS",
  "os_slug": "unique_os",
  "slug": "unique_os_7",
  "version": "7",
  "installer": "ipxe",
  "installer_data": "{\"chain\": \"https://boot.example.com/unique_os_installer.ipxe\"}"
}

How are existing users impacted? What migration steps/scripts do we need?

No changes to existing functionality, only adding additional support.

@codecov
Copy link

codecov bot commented Aug 10, 2021

Codecov Report

Merging #192 (1f966ce) into master (94f4394) will increase coverage by 0.56%.
The diff coverage is 72.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #192      +/-   ##
==========================================
+ Coverage   41.38%   41.94%   +0.56%     
==========================================
  Files          41       41              
  Lines        2576     2620      +44     
==========================================
+ Hits         1066     1099      +33     
- Misses       1423     1436      +13     
+ Partials       87       85       -2     
Impacted Files Coverage Δ
job/ipxe.go 0.00% <0.00%> (ø)
job/mock.go 60.91% <0.00%> (-1.92%) ⬇️
packet/models.go 78.00% <ø> (ø)
installers/custom_ipxe/main.go 100.00% <100.00%> (+30.00%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 94f4394...1f966ce. Read the comment docs.

@jacobweinstock
Copy link
Member

jacobweinstock commented Aug 10, 2021

Hey @mikemrm, I'm not seeing how this is different than the existing custom_ipxe? custom_ipxe can already handle ipxe scripts via a URL to chain load or just a custom IPXE script.

Also, I've put out a proposal that includes deprecating all installers besides a default. I believe there has been an unofficial desire amongst the community to deprecate all installers too.

@mikemrm
Copy link
Contributor Author

mikemrm commented Aug 10, 2021

Correct @jacobweinstock however this differs in how it is triggered. The custom_ipxe is only used when an OperatingSystem distro is set to custom_ipxe which it then looks at a field attached to the instance ipxe_script_url.

The trouble is that this process is only triggered due to the distro being custom_ipxe so if we needed to support a new OS which would have a distro of My OS (my_os) which needed to chain load an ipxe script. We would have to overwrite the distro field sent to cacher/tink with custom_ipxe which is undesireable.

This change makes this path more direct by not relying on faking / overriding a field.

@mikemrm
Copy link
Contributor Author

mikemrm commented Aug 10, 2021

Also, I've put out a proposal that includes deprecating all installers besides a default. I believe there has been an unofficial desire amongst the community to deprecate all installers too.

I agree with that desire and I believe this functionality would allow us to remove the other installers leaving only this method.

@jacobweinstock
Copy link
Member

jacobweinstock commented Aug 10, 2021

@mikemrm , there are actually 2 fields that can trigger a Boots Installer. metadata.instance.operating_system.slug and metadata.instance.operating_system.distro. slug is checked first and then distro is checked.

If I understand the use-case correctly, then the slug can be set to custom_ipxe, and the distro can be anything (my_os) for example.

@mikemrm
Copy link
Contributor Author

mikemrm commented Aug 10, 2021

Correct, it does appear custom_ipxe is actually only being registered for a slug. Meaning slug is the field that would need to be set.

This however doesn't change the issue, as slugs are typically understood as a unique string. Which in this case is the Operating System Version slug which an OS may have many OSVs.

Copy link
Contributor

@mmlb mmlb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chaining and script can both be done with custom_ipxe, why do we need this instead of custom_ipxe?

@mmlb
Copy link
Contributor

mmlb commented Aug 10, 2021

chaining and script can both be done with custom_ipxe, why do we need this instead of custom_ipxe?

well I should refresh before commenting it seems

@jacobweinstock
Copy link
Member

@mikemrm and I talked offline. (@mikemrm correct me if I misspeak here at all!) Here's the synopsis. There is a desire to add an additional hardware data field metadata.instance.operatiing_system.Installer that will be checked before slug and distro. Also, there is a desire to add another new add hardware data field: metadata.instance.operatiing_system.IntallerData. This field would be a string but expected to be a JSON string of {"chain": "", "script":""}.
The reasoning behind this is that it makes sense for the custom business logic internal to Equinix metal that interacts with hardware data via tink/cacher. As the hardware data model in Tink is arbitrary and not enforced, I don't see why this shouldn't be allowed. I did ask Mike to compromise and not create a new Boots Installer but to modify custom_ipxe without breaking backwards compatibility. Mike said he will see what he can do and update this PR.

@mikemrm mikemrm force-pushed the add-generic-ipxe-installer branch from b895f56 to 19bd269 Compare August 10, 2021 21:12
@mmlb
Copy link
Contributor

mmlb commented Aug 10, 2021

Do we really need support for both chain and script fields? Chain can already by done by filling into script.

@mikemrm
Copy link
Contributor Author

mikemrm commented Aug 10, 2021

Do we really need support for both chain and script fields? Chain can already by done by filling into script.

Script was included to make it easier to push an exact script rather than relying on an externally hosted one. This is just a beneficial addition I can see useful for many things including testing without relying on external services.

@mikemrm mikemrm requested a review from mmlb August 10, 2021 21:44
packet/models.go Outdated Show resolved Hide resolved
installers/custom_ipxe/main.go Outdated Show resolved Hide resolved
installers/custom_ipxe/main.go Outdated Show resolved Hide resolved
installers/custom_ipxe/main.go Outdated Show resolved Hide resolved
installers/custom_ipxe/main.go Outdated Show resolved Hide resolved
installers/custom_ipxe/main.go Show resolved Hide resolved
installers/custom_ipxe/main.go Show resolved Hide resolved
@jacobweinstock
Copy link
Member

I think it's important to note the following. This functionality can already be accomplished via the existing custom_ipxe Installer. In my opinion, what this PR does do is create a more clear mapping from hardware data to Boots installer.
New:
metadata.instance.operating_system.installer -> Boots Installer
Existing:
metadata.instance.operating_system.slug -> Boots Installer
metadata.instance.operating_system.distro -> Boots Installer

@mikemrm mikemrm force-pushed the add-generic-ipxe-installer branch 2 times, most recently from 2216b3d to 2c9cfa4 Compare August 11, 2021 14:17
@mikemrm mikemrm requested a review from jacobweinstock August 11, 2021 14:20
Copy link
Member

@jacobweinstock jacobweinstock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for being open to change so much!

installers/custom_ipxe/main.go Outdated Show resolved Hide resolved
jacobweinstock
jacobweinstock previously approved these changes Aug 11, 2021
Copy link
Member

@jacobweinstock jacobweinstock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a DCO issue to address and this should be good to go!

@mmlb
Copy link
Contributor

mmlb commented Aug 11, 2021

Do we really need support for both chain and script fields? Chain can already by done by filling into script.

Script was included to make it easier to push an exact script rather than relying on an externally hosted one. This is just a beneficial addition I can see useful for many things including testing without relying on external services.

Right, so why don't we just support only script and if the use wants to chain they can provide the script that does so.

@mikemrm mikemrm requested a review from jacobweinstock August 11, 2021 15:45
@mikemrm
Copy link
Contributor Author

mikemrm commented Aug 11, 2021

Do we really need support for both chain and script fields? Chain can already by done by filling into script.

Script was included to make it easier to push an exact script rather than relying on an externally hosted one. This is just a beneficial addition I can see useful for many things including testing without relying on external services.

Right, so why don't we just support only script and if the use wants to chain they can provide the script that does so.

Besides the fact that it supports the two code paths of the original custom_ipxe method. I think it also makes an easy way to do the chain loading and adds minimal code. However if you feel this cannot be, I can remove.

@mmlb
Copy link
Contributor

mmlb commented Aug 11, 2021

Do we really need support for both chain and script fields? Chain can already by done by filling into script.

Script was included to make it easier to push an exact script rather than relying on an externally hosted one. This is just a beneficial addition I can see useful for many things including testing without relying on external services.

Right, so why don't we just support only script and if the use wants to chain they can provide the script that does so.

Besides the fact that it supports the two code paths of the original custom_ipxe method. I think it also makes an easy way to do the chain loading and adds minimal code. However if you feel this cannot be, I can remove.

Yeah I think this makes sense, it also makes explicit what was implicit before (either you feed in an ipxe script or we'll chainload what is presumably a url).

@mikemrm mikemrm force-pushed the add-generic-ipxe-installer branch from cefd35e to 0d7fb58 Compare August 11, 2021 20:07
@mikemrm mikemrm requested a review from mmlb August 11, 2021 20:21
jacobweinstock
jacobweinstock previously approved these changes Aug 11, 2021
Comment on lines 19 to 22
logger := installers.Logger("custom_ipxe")
if j.InstanceID() != "" {
logger = logger.With("instance.id", j.InstanceID())
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the job should have its own logger already with these fields (and more) already populated, so this should just be:

logger := job.logger.With("installer", "custom_ipxe")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. updating

func bootScript(j job.Job, s *ipxe.Script) {
func ipxeScript(j job.Job, s *ipxe.Script) {
logger := installers.Logger("custom_ipxe")
if j.InstanceID() != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm we should probably return a s.Shell if there's no instance id right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point, will update.

if cfg == nil {
s.Echo("Installer data not provided")
s.Shell()
logger.Error(err, "empty installer data")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

err is currently nil here, actually this looks like the only use of err at all so no need for var err error right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, with all the changes this is no longer needed.

Signed-off-by: Mike Mason <mason@packet.com>
Signed-off-by: Mike Mason <mason@packet.com>
Signed-off-by: Mike Mason <mason@packet.com>
Signed-off-by: Mike Mason <mason@packet.com>
Signed-off-by: Mike Mason <mason@packet.com>
Signed-off-by: Mike Mason <mason@packet.com>
@mikemrm mikemrm force-pushed the add-generic-ipxe-installer branch from 48fdcab to 1f966ce Compare August 11, 2021 21:41
@displague
Copy link
Member

I'm late to the party, but I have brought up previously that a data uri should be supported as a way to offer ipxe scripts. #110

This would be a minimal change that introduces added functionality within the existing spec.

Thoughts, @mikemrm?

@mikemrm
Copy link
Contributor Author

mikemrm commented Aug 12, 2021

That is an interesting idea @displague however I'm not sure how common it is for someone to want to data uri encode a script and would be interested to see others thoughts about supporting it.

@mikemrm mikemrm requested a review from mmlb August 12, 2021 14:02
@displague
Copy link
Member

displague commented Aug 12, 2021

however I'm not sure how common it is for someone to want to data uri encode a script

I imagine users are not writing templates by hand and would use CLIs and APIs to get the value in place.

We have a URL field and data URLs are an established pattern for providing any data as a URL. Rather than fetch the resource over HTTP, ftp, nntp, gopher, we have the data contained in the URL and the field can be said to support HTTP, HTTPS, and data URLs.

Existing APIs and CLIs that wrap the Tinkerbell API and pass the IPXE URL to Tinkerbell will get this functionality for free (they won't need to be updated for a changed Tinkerbell spec).

Comment on lines +33 to +34
} else if j.IPXEScriptURL() != "" {
cfg = &packet.InstallerData{Chain: j.IPXEScriptURL()}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To illustrate how we could benefit from Data URL handling (using https://pkg.go.dev/github.com/vincent-petithory/dataurl#example-DecodeString):

} else if dataURL, err := dataurl.DecodeString(j.IPXEScriptURL()); err == nil {
    // I assume err is returned unless there is a real data URL, but we might need different logic to ensure the URL was actually a data URL and not just empty
    cfg = &packet.InstallerData{Script: dataURL.Data}
} else if j.IPXEScriptURL() != "" {
...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@displague lets give it a go in a different PR.

@mmlb mmlb added the ready-to-merge Signal to Mergify to merge the PR. label Aug 12, 2021
@mergify mergify bot merged commit 78d4f74 into tinkerbell:master Aug 12, 2021
@mikemrm mikemrm deleted the add-generic-ipxe-installer branch August 12, 2021 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready-to-merge Signal to Mergify to merge the PR.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants